<template>
  <span style="width: 100%">
    <Select
      v-bind="$attrs"
      v-model="value"
      size="small"
      :multiple="multiple"
      :allow-search="allowSearch || isSearch"
      :loading="loading"
      :options="options"
      :filter-option="filterOption"
      @change="onChange"
      @search="onSearch"
    >
      <template v-for="(item, name) in $slots" :key="name" #[name]="dataObj">
        <slot :name="name" v-bind="dataObj || {}"></slot>
      </template>
    </Select>
  </span>
</template>

<script lang="ts" setup>
  import {
    onMounted,
    ref,
    defineEmits,
    watch,
    defineProps,
    computed,
  } from 'vue';
  import { Select } from '@arco-design/web-vue';
  import { apiSelectProps } from './props';
  import type { OptionItemSelect } from './props';

  const emit = defineEmits(['update:modelValue', 'change']);
  const props = defineProps(apiSelectProps);
  const value = ref<string | number | undefined | OptionItemSelect | any[]>('');
  const options = ref<OptionItemSelect[]>([]);

  const filterOption = computed(() => {
    if (props.allowSearch) {
      if (props.searchType === 'remote') {
        return false;
      }
      return true;
    }
    return false;
  });

  const getOptions = (searchValue?: any) => {
    let fun;
    if (props.remoteApi) {
      const paramsObj = {
        ...(props.params || {}),
        searchValue,
      };
      fun = props.remoteApi(paramsObj);
      fun
        .then((res: any) => {
          let list = res;
          if (props.resultField) {
            list = res[props.resultField];
          }
          if (list?.length) {
            options.value = list.map(
              (t: any): OptionItemSelect => {
                return {
                  ...t,
                  value: t[props.valueField],
                  label: `${t[props.labelField]}`,
                };
              }
            );
          } else {
            options.value = [];
          }
        })
        .finally(() => {
          loading.value = false;
        });
    }
  };

  const getValue = (newVal: any) => {
    if (props.valueType === 'object') {
      if (!newVal && !props.multiple) return undefined;
      if (!newVal && props.multiple) return [];

      if (
        Object.prototype.toString.call(newVal) !== '[object Object]' &&
        !props.multiple
      ) {
        return {};
      }

      if (
        Object.prototype.toString.call(newVal) !== '[object Array]' &&
        props.multiple
      ) {
        return [];
      }

      if (!props.multiple) {
        return newVal?.[props.modelValueField];
      }

      return newVal.map((t: any) => t[props.modelValueField]);
    }
    return newVal;
  };

  const getObjectResultItem = (item: any) => {
    return options.value.find((t) => t.value === item);
  };

  const getObjectResult = () => {
    if (!props.multiple) return getObjectResultItem(value.value);

    return (value.value as any[]).map(getObjectResultItem);
  };

  const getResult = () => {
    if (!value.value) return value.value;
    if (props.valueType !== 'object') return value.value;

    return getObjectResult();
  };

  watch(
    () => props.modelValue,
    (newVal) => {
      value.value = getValue(newVal);
    },
    { immediate: true }
  );

  watch(
    () => props.params,
    () => {
      if (props.remoteApi) {
        getOptions();
      }
    },
    { deep: true }
  );

  const onChange = () => {
    const result = JSON.parse(JSON.stringify(getResult()));

    emit('update:modelValue', result);
    emit(
      'change',
      result,
      props.valueType !== 'object'
        ? JSON.parse(JSON.stringify(getObjectResult() || {}))
        : result
    );
  };

  const loading = ref(false);
  const onSearch = async (val: string) => {
    if (props.searchType === 'remote') {
      if (val) {
        loading.value = true;
        await getOptions(val);
      } else {
        options.value = [];
      }
    }
  };

  onMounted(() => {
    if (props.isSearch) {
      return;
    }
    if (!props.immediate) {
      return;
    }
    getOptions();
  });
</script>
